using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Abp.Dependency;
using Microsoft.AspNetCore.Http;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using Yozeev.IO.ExcelHelper.Attributes;

namespace Yozeev.IO.ExcelHelper
{
    /// <summary>
    /// Excel操作的工具类，目前只实现了读取的方法
    /// </summary>
    public class ExcelHelper : IExcelHelper, ITransientDependency
    {
        /// <summary>
        /// 读取Excel并且返回单个实体
        /// </summary>
        /// <typeparam name="T">实体的类型</typeparam>
        /// <param name="file">abp 的IFormFile类型</param>
        /// <returns></returns>
        public T Read<T>(IFormFile file)
        {
            return Read<T>(file.OpenReadStream());
        }

        /// <summary>
        /// 读取Excel并且返回单个实体
        /// </summary>
        /// <typeparam name="T">实体的类型</typeparam>
        /// <param name="fileStream">文件流</param>
        /// <returns></returns>
        public T Read<T>(Stream fileStream)
        {
            //获取T的属性
            Type t = typeof(T);
            int headerLine = GetHeadLine(t);

            //获取T的字段
            Dictionary<int, string> propDictionary = GetProperties(t, out PropertyInfo[] props);

            //根据T的属性读取文件信息
            XSSFWorkbook workbook = new XSSFWorkbook(fileStream);
            XSSFSheet sheet = (XSSFSheet)workbook.GetSheetAt(0);
            IRow row = sheet.GetRow(headerLine);

            //返回T
            return RowToEntity<T>(t, row, propDictionary, props);
        }

        /// <summary>
        /// 读取Excel并且返回单个实体
        /// </summary>
        /// <typeparam name="T">实体的类型</typeparam>
        /// <param name="filepath">文件路径</param>
        /// <returns></returns>
        public T Read<T>(string filepath)
        {
            if (!File.Exists(filepath)) throw new FileNotFoundException();
            return Read<T>(File.OpenRead(filepath));
        }

        /// <summary>
        /// 读取Excel并将结果转成实体的列表返回
        /// </summary>
        /// <typeparam name="T">实体的类型</typeparam>
        /// <param name="file">abp 的IFormFile类型</param>
        /// <returns></returns>
        public List<T> ReadToList<T>(IFormFile file)
        {
            if(file.FileName.Contains("xlsx"))
                return ReadToList<T>(file.OpenReadStream());
            else
                return ReadToList<T>(file.OpenReadStream(),"xls");
        }

        /// <summary>
        /// 读取Excel并将结果转成实体的列表返回
        /// </summary>
        /// <typeparam name="T">实体的类型</typeparam>
        /// <param name="fileStream">文件流</param>
        /// <returns></returns>
        public List<T> ReadToList<T>(Stream fileStream,string Suffix= "xlsx")
        {
            //获取T的属性
            Type t = typeof(T);
            int headerLine = GetHeadLine(t);
            //获取T的字段
            Dictionary<int, string> propDictionary = GetProperties(t, out PropertyInfo[] props);
            List<T> result = new List<T>();
            ISheet sheet;
            //根据T的属性读取文件信息
            //if (Suffix!= "xlsx")
            //{
                XSSFWorkbook workbook = new XSSFWorkbook(fileStream);
                sheet = (XSSFSheet)workbook.GetSheetAt(0);
            //}else
            //{
            //    HSSFWorkbook workbook = new HSSFWorkbook(fileStream);
            //    sheet = (HSSFSheet)workbook.GetSheetAt(0);
            //}
            for (int i = headerLine; i <= sheet.LastRowNum; i++)
            {
                IRow row = sheet.GetRow(i);
                T item = RowToEntity<T>(t, row, propDictionary, props);
                result.Add(item);
            }
            //返回T
            return result;
        }

        /// <summary>
        /// 读取Excel并将结果转成实体的列表返回
        /// </summary>
        /// <typeparam name="T">实体的类型</typeparam>
        /// <param name="filepath">文件路径</param>
        /// <returns></returns>
        public List<T> ReadToList<T>(string filepath)
        {
            if (!File.Exists(filepath)) throw new FileNotFoundException();
            return ReadToList<T>(File.OpenRead(filepath));
        }

        /// <summary>
        /// 获取Excel表格的值
        /// 根据属性的不同返回不同类型的值
        /// </summary>
        /// <param name="propType"></param>
        /// <param name="cell"></param>
        /// <returns></returns>
        private object GetCellValue(Type propType, ICell cell)
        {
            if (propType == typeof(string))
            {
                try
                {
                    return cell.StringCellValue;
                }
                catch (InvalidOperationException)
                {
                    return cell.NumericCellValue.ToString();
                }
            }
            if (propType == typeof(bool) || propType == typeof(bool?))
                return cell.BooleanCellValue;
            if (propType == typeof(DateTime) || propType == typeof(DateTime?))
                return cell.DateCellValue;
            if (propType == typeof(float) || propType == typeof(float?))
                try
                {
                    return (float)cell.NumericCellValue;
                }
                catch (InvalidOperationException)
                {
                    return float.Parse(cell.StringCellValue);
                }
            if (propType == typeof(int) || propType == typeof(int?))
                try
                {
                    return (int)cell.NumericCellValue;
                }
                catch (InvalidOperationException)
                {
                    return int.Parse(cell.StringCellValue);
                }
            if (propType == typeof(long) || propType == typeof(long?))
                try
                {
                    return (long)cell.NumericCellValue;
                }
                catch (InvalidOperationException)
                {
                    return long.Parse(cell.StringCellValue);
                }
            if (propType.IsEnum)
                return (Int32)cell.NumericCellValue;
            return cell.NumericCellValue;
        }

        /// <summary>
        /// 读取excelmodel的headerline属性
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        private int GetHeadLine(Type t)
        {
            ExcelModel excelModel = (ExcelModel)Attribute.GetCustomAttribute(t, typeof(ExcelModel));
            int headerLine = 1; //表头行数，要根据表头行数跳过表头的读取，默认为1
            if (excelModel != null)
            {
                headerLine = excelModel.HeaderLine;
            }

            return headerLine;
        }

        /// <summary>
        /// 根据类型以及类型上的注解，构建类型属性的字典
        /// </summary>
        /// <param name="t"></param>
        /// <param name="props"></param>
        /// <returns></returns>
        private Dictionary<int, string> GetProperties(Type t, out PropertyInfo[] props)
        {
            Dictionary<int, string> propDictionary = new Dictionary<int, string>();
            props = t.GetProperties();
            if (props.Length > 0)
            {
                foreach (PropertyInfo prop in props)
                {
                    object[] attrs = prop.GetCustomAttributes(true);
                    foreach (var attr in attrs)
                    {
                        ExcelColumn columnProp = attr as ExcelColumn;
                        if (columnProp != null)
                        {
                            string propName = prop.Name;
                            int columnIndex = columnProp.ColumnIndex;
                            propDictionary.Add(columnIndex, propName);
                        }
                    }
                }
            }
            else
            {
                //如果字段不存在，或者字段上没有注解，就到属性上读取注解
                FieldInfo[] fields = t.GetFields();
                foreach (var field in fields)
                {
                    object[] fieldAttributes = field.GetCustomAttributes(true);
                    foreach (var attr in fieldAttributes)
                    {
                        ExcelColumn columnProp = attr as ExcelColumn;
                        string propName = field.Name;
                        int columnIndex = columnProp.ColumnIndex;

                        propDictionary.Add(columnIndex, propName);
                    }
                }
            }

            return propDictionary;
        }

        /// <summary>
        /// 将表格的行转为实体
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="t">实体类型</param>
        /// <param name="row">当前行</param>
        /// <param name="propDictionary">实体属性字典</param>
        /// <param name="props">实体属性</param>
        /// <returns></returns>
        private T RowToEntity<T>(Type t, IRow row, Dictionary<int, string> propDictionary, PropertyInfo[] props)
        {
            T instance = (T)Activator.CreateInstance(t);
            foreach (KeyValuePair<int, string> entry in propDictionary)
            {
                if (props.Length > 0)
                {
                    PropertyInfo piInstance = t.GetProperty(entry.Value);
                    Type propTyp = piInstance.PropertyType;
                    if (propTyp.IsEnum)
                    {
                        piInstance.SetValue(instance, Enum.ToObject(propTyp, GetCellValue(piInstance.PropertyType, row.GetCell(entry.Key))));
                    }
                    else
                    {
                        piInstance.SetValue(instance, GetCellValue(piInstance.PropertyType, row.GetCell(entry.Key)));
                    }
                }
                else
                {
                    FieldInfo field = t.GetField(entry.Value);
                    field.SetValue(instance, GetCellValue(field.FieldType, row.GetCell(entry.Key)));
                }
            }

            //返回T
            return instance;
        }
    }
}
